package com.googlecode.restitory.gae;

import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;

public class RequestJsoner {

	public static String formatJson(Request req) {
		JsonObject obj = formatJsonElement(req);
		String ret = getBuilder().create().toJson(obj);
		return ret;
	}

	public static JsonObject formatJsonElement(Request req) {
		if (req == null) {
			return null;
		}
		JsonObject ret = new JsonObject();
		if (req.getType() != null) {
			ret.addProperty("type", req.getType().name());
		}
		if (req.getPath() != null) {
			ret.addProperty("path", req.getPath());
		}
		ret.add("params", formatJsonElement(req.getParams()));
		ret.add("headers", formatJsonElement(req.getHeaders()));
		ret.add("content", formatJsonElement(req.getContent()));

		return ret;
	}

	public static JsonObject formatJsonElement(Content content) {
		if (content == null) {
			return null;
		}
		JsonObject ret = new JsonObject();
		if (content instanceof TextContent) {
			ret.addProperty("type", "text");
		} else {
			ret.addProperty("type", "binary");
		}
		ret.addProperty("value", content.toString());
		return ret;
	}

	public static JsonArray formatJsonElement(NamePairs params) {
		List<NamePair> pairs = params.getPairs();
		JsonArray ret = new JsonArray();
		for (NamePair pair : pairs) {
			JsonObject entry = new JsonObject();
			entry.addProperty("name", pair.getName());
			entry.addProperty("value", pair.getValue());
			ret.add(entry);
		}
		return ret;
	}

	public static GsonBuilder getBuilder() {
		return new GsonBuilder();
	}

	public static Request parseRequestJson(String json) {
		JsonParser parser = new JsonParser();
		JsonObject parsed = (JsonObject) parser.parse(json);
		return parseRequestJson(parsed);
	}

	public static Request parseRequestJson(JsonObject obj) {
		Request ret = new Request();
		if (obj.has("type")) {
			ret.setType(Type.valueOf(obj.getAsJsonPrimitive("type")
					.getAsString()));
		}
		if (obj.has("path")) {
			ret.setPath(obj.getAsJsonPrimitive("path").getAsString());
		}
		if (obj.has("params")) {
			JsonElement element = obj.get("params");
			if (element instanceof JsonArray) {
				parsePairsJson((JsonArray) element, ret.getParams());
			}
			if (element instanceof JsonObject) {
				parsePairsJson((JsonObject) element, ret.getParams());
			}
		}
		if (obj.has("headers")) {
			JsonElement element = obj.get("headers");
			if (element instanceof JsonArray) {
				parsePairsJson((JsonArray) element, ret.getHeaders());
			}
			if (element instanceof JsonObject) {
				parsePairsJson((JsonObject) element, ret.getHeaders());
			}
		}
		if (obj.has("content")) {
			ret.setContent(parseContentJson(obj.getAsJsonObject("content")));
		}
		return ret;
	}

	public static void parsePairsJson(JsonObject obj, NamePairs params) {
		Set<Entry<String, JsonElement>> set = obj.entrySet();
		for (Entry<String, JsonElement> entry : set) {
			String name = entry.getKey();
			JsonElement value = entry.getValue();
			if (value instanceof JsonArray) {
				JsonArray array = (JsonArray) value;
				for (JsonElement arrayElement : array) {
					params.add(name, arrayElement.getAsString());
				}
			} else {
				JsonPrimitive primitive = (JsonPrimitive) value;
				params.add(name, primitive.getAsString());
			}
		}
	}

	public static Content parseContentJson(JsonObject object) {
		String type = object.getAsJsonPrimitive("type").getAsString();
		String value = object.getAsJsonPrimitive("value").getAsString();
		if (type.equals("text")) {
			return new TextContent(value);
		} else if (type.equals("binary")) {
			return new BinaryContent().parse(value);
		} else {
			throw new RuntimeException("type unknown: " + type);
		}
	}

	public static void parsePairsJson(JsonArray array, NamePairs pairs) {
		for (JsonElement element : array) {
			JsonObject obj = (JsonObject) element;
			String name = obj.getAsJsonPrimitive("name").getAsString();
			String value = obj.getAsJsonPrimitive("value").getAsString();
			pairs.add(name, value);
		}
	}

	public static String formatJson(Response resp) {
		JsonObject obj = formatJsonElement(resp);
		String ret = getBuilder().create().toJson(obj);
		return ret;
	}

	public static JsonObject formatJsonElement(Response resp) {
		JsonObject ret = new JsonObject();
		ret.add("request", formatJsonElement(resp.getRequest()));
		ret.addProperty("code", resp.getCode());
		ret.addProperty("message", resp.getMessage());
		ret.add("headers", formatJsonElement(resp.getHeaders()));
		ret.add("content", formatJsonElement(resp.getContent()));
		return ret;
	}

	public static Response parseResponseJson(String json) {
		JsonParser parser = new JsonParser();
		JsonObject obj = (JsonObject) parser.parse(json);
		return parseResponseJson(obj);
	}

	public static Response parseResponseJson(JsonObject obj) {
		Response ret = new Response();
		if (obj.has("request")) {
			ret.setRequest(parseRequestJson(obj.getAsJsonObject("request")));
		}
		if (obj.has("code")) {
			ret.setCode(obj.getAsJsonPrimitive("code").getAsNumber().intValue());
		}
		if (obj.has("message")) {
			ret.setMessage(obj.getAsJsonPrimitive("message").getAsString());
		}
		if (obj.has("headers")) {
			JsonElement element = obj.get("headers");
			if (element instanceof JsonArray) {
				parsePairsJson((JsonArray) element, ret.getHeaders());
			}
			if (element instanceof JsonObject) {
				parsePairsJson((JsonObject) element, ret.getHeaders());
			}
		}
		if (obj.has("content")) {
			ret.setContent(parseContentJson(obj.getAsJsonObject("content")));
		}
		return ret;
	}
}
